classdef ziDaq < EquipmentControl.ZI.ziModule.ziModule
    %ZIDAQ Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        available_sample_name
        available_trigger_name
    end
    
    methods
        function obj = ziDaq(dev)
            %ZISPECTRUM Construct an instance of this class
            %   Detailed explanation goes here
            obj@EquipmentControl.ZI.ziModule.ziModule('daq', dev);
            
            % Control tab - only time domain demod channels are allowed -
            % improve later
            obj.available_sample_name = {'sample.AuxIn0', ...
                                        'sample.AuxIn1', ...
                                        'sample.Dio', ...
                                        'sample.Frequency', ...
                                        'sample.R', ...
                                        'sample.Theta', ...
                                        'sample.TrigDemod1Phase', ...
                                        'sample.TrigDemod2Phase', ...
                                        'sample.TrigIn1', ...
                                        'sample.TrigIn2', ...
                                        'sample.TrigOut1', ...
                                        'sample.TrigOut2', ...
                                        'sample.X', ...
                                        'sample.Y'};
                                    
            % trigger settings - some settings are constrained as below, improve later
            obj.available_trigger_name = {'sample.AuxIn0', ...
                                        'sample.AuxIn1', ...
                                        'sample.Dio', ...
                                        'sample.Frequency', ...
                                        'sample.R', ...
                                        'sample.Theta', ...
                                        'sample.TrigIn1', ...
                                        'sample.TrigIn2', ...
                                        'sample.TrigOut1', ...
                                        'sample.TrigOut2', ...
                                        'sample.X', ...
                                        'sample.Y'};
            h = obj.hdl;
            ziDAQ('set', h, 'dataAcquisitionModule/preview', 1);
            % grid tab - some settings are constrained as below, improve later
            ziDAQ('set', h, 'dataAcquisitionModule/grid/mode', 4); % mode = 4 Exact (on-grid)
            ziDAQ('set', h, 'dataAcquisitionModule/grid/rows', 1); % row number = 1
            ziDAQ('set', h, 'dataAcquisitionModule/grid/direction', 0); % foward scan
            ziDAQ('set', h, 'dataAcquisitionModule/grid/repetitions', 2); % repetition number = 2 => to prevent data loss at very beginning
            ziDAQ('set', h, 'dataAcquisitionModule/grid/waterfall', 0); % disable waterfall
            ziDAQ('set', h, 'dataAcquisitionModule/grid/overwrite', 0); % disable overwrite
            ziDAQ('set', obj.hdl, 'dataAcquisitionModule/clearhistory', 1);
            ziDAQ('set', obj.hdl, 'dataAcquisitionModule/clearhistory', 1);
            ziDAQ('unsubscribe', obj.hdl, '*');
           
        end

        %% Set Parameters
        function forceTrigger(obj)
            L=log4m.getLogger();
            ziDAQ('set', obj.hdl, 'dataAcquisitionModule/forcetrigger', 1);
            L.info('ziDaq::forceTrigger', 'Force triggered.');
        end
        
        function setColNum(obj, num)
            obj.setParam('grid/cols', num); 
        end
        
        function setTriggerSource(obj, ch, node)
            path = ['/' obj.device '/demods/' ch '/sample.' node];
            obj.setParam('triggernode', path);
        end
        function setTriggerEdge(obj, index) % 1 - positive, 2 - negative, 3 - both
            obj.setParam('edge', index);
        end
        function setTriggerLevel(obj, level)
            obj.setParam('level', level);
        end
        function setTriggerHysteresis(obj, level)
            obj.setParam('hysteresis', level);
        end
        function setTriggerHoldoff(obj, t)
            obj.setParam('holdoff/time', t);
        end
        function setTriggerDelay(obj, t)
            obj.setParam('delay', t);
        end
        
        %% Run
        function res = run(obj, varargin)
            clear textprogressbar;
            
            p=inputParser;
            func_hdl_test = @(x) isa(x, 'function_handle') || strcmp(x, 'none');
            p.addParameter('processMonitor', @plotData, func_hdl_test);
            p.parse(varargin{:});
            
            if ~isvalid(obj.monitor_hdl)
                obj.createMonitor;
            end
            
            processMonitor = p.Results.processMonitor;
            obj.monitor_plot_init();
            
            obj.enableSingleShot();
            obj.executeModule();
            
            textprogressbar( sprintf('%s: Acquiring Data,\t', datestr(now,'HH:MM:SS.FFF, mm/dd/yy')) );
            while  ~obj.finishedModule()
                pause(0.1);
                textprogressbar(obj.progressModule()*100);
                
                tmp = obj.read();
                if ziCheckPathInData(tmp, obj.subscribed_paths{1}.path_channel_string)
                    daqData = EquipmentControl.ZI.ziData.ziDaqData(tmp);
                    data = arrayfun(@(curve) daqData.getNodeData(curve.channel, curve.node, 1), obj.curve_info);
                    processMonitor(obj, obj.monitor_hdl, data);
                end                
            end
            res = EquipmentControl.ZI.ziData.ziDaqData(tmp);
            textprogressbar( 100 ); textprogressbar('.\t done.');

            tmp = obj.read();
            if ziCheckPathInData(tmp, obj.subscribed_paths{1}.path_channel_string)
                daqData = EquipmentControl.ZI.ziData.ziDaqData(tmp);
                data = arrayfun(@(curve) daqData.getNodeData(curve.channel, curve.node, 1), obj.curve_info);
                processMonitor(obj, obj.monitor_hdl, data);
                res = daqData;
            end            
            obj.measured_data = res;
            
            obj.finish();
        end        
        
        %% Subscribe/Unsubscribe node
        function obj = subscribeChannel(obj, channel, sample_names)
            % subscribe channels
            L=log4m.getLogger;
            p = inputParser;
            chTest = @(x) ismember(channel, obj.available_demod_channel);
            sampleTest = @(x) all(ismember(sample_names, obj.available_sample_name));
            p.addRequired('channel', chTest);
            p.addRequired('sample_name', sampleTest);
            p.parse(channel, sample_names);
            
            obj.zi.enableDataTransfer(channel);
            paths = obj.make_path(channel, sample_names);

            channelPath = paths{1}.path_string;
            obj.subscribeNode(channelPath);
            L.info('ziSpectrum::subscribeChannel', sprintf('Node %s subscribed.', channelPath));
        end
        
        function obj = unsubscribeChannel(obj)
            % unsubscribe channels
            L=log4m.getLogger;
            subs_ch = unique( cellfun(@(p) p.path_string ,obj.subscribed_paths, 'UniformOutput', false) );
            for k = 1:length(subs_ch)
              obj.unsubscribeNode(subs_ch{k});
              L.info('ziSpectrum::unsubscribeChannel', sprintf('Node %s unsubscribed.', subs_ch{k}));
            end
            obj.syncModule();
        end

        function obj = enableSingleShot(obj)
            enabled = false;
            while ~enabled
                ziDAQ('set', obj.hdl, 'dataAcquisitionModule/endless', 0); 
                res = ziDAQ('get', obj.hdl, 'dataAcquisitionModule/endless'); 
                enabled = (res.endless == 0);
            end
        end
%% monitor 
        function plotData(obj, ~, data)
            for k=1:length(obj.curve_info)
                obj.curves(k).XData = data(k).time;
                obj.curves(k).YData = data(k).value;
            end
            drawnow limitrate     
        end
        
        function monitor_plot_init(obj)
            color_array = obj.monitor_hdl.ColorOrder;
            len_color = size(color_array, 1);
            obj.curves = arrayfun(@(k) line(obj.monitor_hdl, ...
                                           'Color', color_array(mod(k, len_color)+1,:), ...
                                           'DisplayName', obj.curve_info(k).str), ...
                                 1:length(obj.curve_info));
            obj.monitor_hdl.YScale = 'linear';
            legend('show');
        end        
    end
    %% private
    methods (Access = private)
        function paths = make_path(obj, ch, sample)
            if ~iscell(sample)
                sampleCell = {sample};
            else
                sampleCell = sample;
            end
            
            paths = cellfun(@(sa) EquipmentControl.ZI.ziData.ziPath(obj.device, 'demods', ch,  sa, ''), sampleCell, 'UniformOutput', false);
            obj.subscribed_paths = [obj.subscribed_paths paths];
            obj.curve_info=cellfun(@(p) p.channel_sample_str, obj.subscribed_paths);
        end
    end
end

